package Verifier.Cryptography;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import Verifier.GeneralUtils;
import Verifier.ByteTree.ByteTree;
import Verifier.ByteTree.ByteTreeElement;
import Verifier.ByteTree.Leaf;

public enum HashFunction {
	// declare the supported hash functions
	SHA256	("SHA-256"),
	SHA384	("SHA-384"),
	SHA512	("SHA-512");
	// the number of bits per hash
	private int numberOfBits;
	
	private HashFunction(String hashName){
		// the number of bits per hash is stated inside the name of the hash, from index 4 to 7
		String s= hashName.substring(4, 7);
		this.numberOfBits = Integer.parseInt(s);
	}
	
	/**
	 * Gets a byte tree which contains one leaf and returns the corresponding hash function. 
	 *  
	 *  @param bt byte tree which contains a leaf
	 *  
	 * If the byte tree contains more than one leaf, return null.
	 * If the Leaf contains String which is not one of the hush functions we support, return null.
	 * 
	 */
	static public HashFunction HashFromByteTree(ByteTree bt){
		// read the hash function from the tree
		ByteTreeElement e = bt.getFirstByteTreeElement();
		// check that the tree is of type we expect - Leaf('[name of hash function]')
		if(!(e instanceof Leaf)){
			/* The byte tree must be a leaf */
			return null;
		}
		// read the name of the hash function
		Leaf l = (Leaf) e;
		String s = GeneralUtils.convertBytesToString(l.getDataByteArray());
		// compare the hash function required with the ones we know - if one is a match, then return it, otherwise return null
		if (s.equals("SHA-256")){
			return SHA256;
		}
		if (s.equals("SHA-384")){
			return SHA384;
		}
		if (s.equals("SHA-512")){
			return SHA512;
		}
		
		/* The data in the byte tree is incorrect. Will never get here */
		return null;
	}
	
	
	/**
	 *  Denotes a hash digest of the byte tree.
	 *  
	 *  @param bt - byte tree to be hashed
	 *  
	 * @return byte array containing the hash digest for bt				
	*/
	public byte[] HashDigest(ByteTree bt) {
		// digest the byte tree's byte array and return the byte array
		return HashDigest(bt.getByteArray());
	}
	
	/**
	 *  Denotes a hash digest of the byte array.
	 *  
	 *  @param ba - byte array to be hashed
	 *  
	 * @return byte array containing the hash digest for ba		
	*/
	public byte[] HashDigest(byte[] ba) {
		try{
			// run the built in function of java to digest the byte array ba
			MessageDigest md;
			md = MessageDigest.getInstance("SHA-" + numberOfBits);
			// reset the message digest object, to clear its input, and update it with the new array we wish to digest
			md.reset();
			md.update(ba);
			
			byte byteData[] = md.digest();
			// return the digested byte array
			return byteData;
		}
		catch (NoSuchAlgorithmException e) {
			return null; /*Will never get here*/
		}
		
	}
	
	/**
	 * @return number of bytes in the hash digest
	 * */
	public int NumberOfBytes() { return this.numberOfBits/8; }
	
}
